package db

import (
	"github.com/go-xorm/xorm"
	"log"
	"os"
	"xorm.io/core"

	_ "github.com/lib/pq"
)

var engine *xorm.Engine

func init() {
	log.SetFlags(log.Ldate | log.Lshortfile)
	var err error
	connStr := "postgres://postgres:ysm@121388@localhost/experiment?sslmode=disable"
	engine, err = xorm.NewEngine("postgres", connStr)
	if err != nil {
		log.Println(err)
	}
	log.Println("开启postgres数据成功")
	f, err := os.OpenFile("sql.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		log.Println(err)
		return
	}
	engine.SetLogger(xorm.NewSimpleLogger(f))
	engine.Logger().SetLevel(core.LOG_DEBUG)
	engine.ShowSQL(true)
	// 设置命名规则，遵从驼峰规则，但是对特殊词支持的更好，使得ID翻译为id而不是i_d,	但是创建表的格式好奇怪啊 select 语句会变成SELECT "ID", "Content", "Version", "UpdateTime", "CreateTime" FROM public."TPhoto";
	// engine.SetMapper(core.SameMapper{})
}

// TPhoto 创建图片表
type TPhoto struct {
	Id         int64  `xorm:"pk notnull unique autoincr"`
	Content    string `xorm:"notnull"`
	Version    int64  `xorm:"version"`
	UpdateTime int64  `xorm:"updated"`
	CreateTime int64  `xorm:"created"`
	Name       string `xorm:"varchar notnull"`
}

// ShowAllTables 显示所有的table
func ShowAllTables() {
	info, err := engine.DBMetas()
	if err != nil {
		log.Println(err)
		return
	}
	for _, v := range info {
		log.Println(v.Name)
	}
}

// createTable 创建表
func createTable() {
	tPhoto := &TPhoto{}
	// 创建表
	isExist, err := engine.IsTableExist("t_photo")
	if err != nil {
		log.Println(err)
		return
	}
	if !isExist {
		err := engine.CreateTables(tPhoto)
		if err != nil {
			log.Println(err)
		}
	}
	// 结构体和表一致,当结构体改变的时候，也能够根据结构体的内容修改表的结构
	err = engine.Sync2(tPhoto)
	if err != nil {
		log.Println(err)
	}
}

// createTableSQL 原生的创建表的方式
func createTableSQL() {
	SQL := "create table t_small_photo(id int primary key,name varchar)"
	isExit, err := engine.IsTableExist("t_small_photo")
	if err != nil {
		log.Println(err)
		return
	}
	if isExit {
		log.Println("t_small_photo已经存在")
	}
	_, err = engine.Exec(SQL)
	if err != nil {
		log.Println(err)
		return
	}
	isExit, err = engine.IsTableExist("t_small_photo")
	if err != nil {
		log.Println(err)
		return
	}
	if isExit {
		log.Println("t_small_photo存在")
	}
}

// dropTable 删除表
func dropTable() {
	tPhoto := &TPhoto{}
	// 判断指定表是否存在
	isExist, err := engine.IsTableExist("t_photo")
	if err != nil {
		log.Println(err)
		return
	}
	if !isExist {
		// 删除指定的表
		err := engine.DropTables(tPhoto)
		if err != nil {
			log.Println(err)
		}
	}
}

// insertData 插入数据库,会根据不同的结构体的名字找到表，进行相应数据的修改
func insertData() {

	// 插入一条数据
	tPhoto := &TPhoto{Content: "content1", Name: "name1"}

	_, err := engine.Insert(tPhoto)
	if err != nil {
		log.Println(err)
		return
	}

	// 插入多条数据
	tPhotos := []*TPhoto{
		{Content: "content2", Name: "name2"},
		{Content: "content3", Name: "name3"},
		{Content: "content4", Name: "name4"},
		{Content: "content5", Name: "name5"},
	}

	_, err = engine.Insert(&tPhotos)
	if err != nil {
		log.Println(err)
	}
}

// insertDataSQL 插入数据库，使用SQL方式
func insertDataSQL() {
	// 这样不会自动生成 插入时间和修改时间
	SQL := "insert into t_photo (content,name) values($1,$2)"
	_, err := engine.Exec(SQL, "content7", "name7")
	if err != nil {
		log.Println(err)
		return
	}
}

// selectData 查询数据
func selectData() {

	/*
		Get 查询一条信息 参数不是map(json格式) 也不是数组
		Find 查询很多条数据 参数是map(json格式) 或者是 数据

	*/

	// 查询第一条数据 select * from 表 limit 1
	tPhoto := &TPhoto{}
	isHas, err := engine.Get(tPhoto)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(isHas)
	log.Println(tPhoto)

	// 毫无差别的查询 select * from 表
	tPhotos := make([]TPhoto, 0)
	err = engine.Find(&tPhotos)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(tPhotos)

	// 按条件查找 获取任一条数据  就是根据结构体的名字和表的名字相对应了 select  * from t_photo where content='content' and name='name',理解起来就是跟sql中条件的表述一样
	tPhotos1 := make([]TPhoto, 0)
	err = engine.Where("content = ?", "content1").And("name =?", "name1").Find(&tPhotos1)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(tPhotos1)

	// 指定查询的数据
	tPhtot1 := &TPhoto{}
	isHas, err = engine.Select("id,content,name").Where("id = ?", 1).Get(tPhtot1)
	if err != nil {
		log.Println(err)
		return
	}

	if isHas {
		log.Println(tPhtot1)
	}
}

// count 统计
func count() {

	tPhtot1 := &TPhoto{}
	num, err := engine.Where("id = ?", 1).Count(tPhtot1)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(num)

}

// updateData  修改数据
func updateData() {

	// 原来是乐观锁带来的小坑，因为使用version标记在修改的时候，update的内容必须包含version
	tPhtot1 := &TPhoto{Content: "content12", Name: "name12", Version: 1}
	affect, err := engine.Where("id = ?", 1).Cols("content,name").Update(tPhtot1)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("affect:", affect)
	tPhotos := make([]TPhoto, 0)
	err = engine.Where("id = ?", 1).Find(&tPhotos)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(tPhotos)
}

func main() {
	log.Println("学习log")

	// 显示所有的表

	// ShowAllTables()

	// createTableSQL()

	// createTable()

	// insertData()

	// insertDataSQL()

	// selectData()

	// count()

	updateData()

}
